﻿#include <learn/gl/gl_context.h>

// 由于 STB_IMAGE_IMPLEMENTATION 将 std_image 被编译为 .cpp
// 文件，因此不放在头文件中
#define STB_IMAGE_IMPLEMENTATION
#include <third/stb_image.h>

using namespace xi;
using namespace xi::gl;

namespace
{

Vec2f g_mousePos{};                  // 全局记录位置
AIS_Context *g_aisContext = nullptr; // 系统上下文

void GLFWWindowResize(GLFWwindow *window, int width, int height)
{
    glViewport(0, 0, width, height);
    g_aisContext->resize_event(width, height);
}

void GLFWMouseButtonCallback(GLFWwindow *window, int button, int action, int mode)
{
    if (action == GLFW_PRESS)
    {
        if (button == GLFW_MOUSE_BUTTON_LEFT)
            g_aisContext->mouse_press_event(g_mousePos, AIS_MouseMsg::Button::Left);
        else if (button == GLFW_MOUSE_BUTTON_RIGHT)
            g_aisContext->mouse_press_event(g_mousePos, AIS_MouseMsg::Button::Right);
        else
            g_aisContext->mouse_press_event(g_mousePos, AIS_MouseMsg::Button::Middle);
    }
    else if (action == GLFW_RELEASE)
        g_aisContext->mouse_release_event(g_mousePos);
}

void GLFWCursorPosCallback(GLFWwindow *window, double xpos, double ypos)
{
    g_mousePos = Vec2f{float(xpos), float(ypos)};
    g_aisContext->mouse_move_event(g_mousePos);
}

void GLFWKeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods)
{
    g_aisContext->key_event(key, scancode, action, mods);
}

void GLFWScrollCallback(GLFWwindow *window, double xoffset, double yoffset)
{
    g_aisContext->mouse_wheel_event(Vec2f{0.0f, float(yoffset)});
}

void GLFWDropCallback(GLFWwindow *window, int path_count, const char **paths)
{
    for (int i = 0; i < path_count; i++)
    {
        std::string path = paths[i];
        std::cerr << "Drop file: " << path << std::endl;
        Mesh_Data mesh;
        mesh.read(path.c_str());
        auto amesh = std::make_shared<AIS_Mesh>(mesh);
        g_aisContext->add_object(amesh);
    }
}

} // namespace

// 必须在这个库里初始化 glfw 函数，不然不能调用 glfw 函数
GL_Context::GL_Context(int hwnd, int width, int height) : m_shader(nullptr)
{
    glfwInit();

    // 配置版本信息，启用核心模式
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    // 启用 4 重采样
    glfwWindowHint(GLFW_SAMPLES, 4);

    if (hwnd != 0)
        m_window = glfwCreateWindowEx(width, height, "GLFW", 0, 0, hwnd);
    else
        m_window = glfwCreateWindow(width, height, "GLFW", 0, 0);

    if (m_window == nullptr)
    {
        glfwTerminate();
        return;
    }
    glfwMakeContextCurrent(m_window);

    // 初始化 GLAD 来管理 OpenGL 函数指针
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
        return;

    // 注册函数
    glfwSetFramebufferSizeCallback(m_window, GLFWWindowResize);
    glfwSetScrollCallback(m_window, GLFWScrollCallback);
    glfwSetMouseButtonCallback(m_window, GLFWMouseButtonCallback);
    glfwSetKeyCallback(m_window, GLFWKeyCallback);
    glfwSetCursorPosCallback(m_window, GLFWCursorPosCallback);
    glfwSetDropCallback(m_window, GLFWDropCallback);

    // 注册交互
    g_aisContext = new AIS_Context(width, height);

    // 注册渲染器生成方法
    g_aisContext->renderer_factory()->register_product("AIS_Point", [this]() { return new GL_PointRenderer(this); });
    g_aisContext->renderer_factory()->register_product("AIS_PointCloud",
                                                       [this]() { return new GL_PointCloudRenderer(this); });
    g_aisContext->renderer_factory()->register_product("AIS_Vector", [this]() { return new GL_VectorRenderer(this); });
    g_aisContext->renderer_factory()->register_product("AIS_Curve", [this]() { return new GL_CurveRenderer(this); });
    g_aisContext->renderer_factory()->register_product("AIS_Mesh", [this]() { return new GL_MeshRenderer(this); });
    g_aisContext->renderer_factory()->register_product("AIS_Surface",
                                                       [this]() { return new GL_SurfaceRenderer(this); });
    g_aisContext->renderer_factory()->register_product("AIS_Frame", [this]() { return new GL_FrameRenderer(this); });

    // 生成默认纹理构造
    gen_tex(Texture::Green2d);
    bind_tex2d(Texture::Green2d);
    load_tex2d(Texture::Green2d);

    gen_tex(Texture::White2d);
    bind_tex2d(Texture::White2d);
    load_tex2d(Texture::White2d);

    gen_tex(Texture::Black2d);
    bind_tex2d(Texture::Black2d);
    load_tex2d(Texture::Black2d);

    gen_tex(Texture::Chess);
    bind_tex2d(Texture::Chess);
    load_tex2d(Texture::Chess);
}

GL_Context::~GL_Context()
{
    for (auto it : m_VAOs)
        glDeleteVertexArrays(1, &it.second);
    for (auto it : m_VBOs)
        glDeleteBuffers(1, &it.second);
    for (auto it : m_EBOs)
        glDeleteBuffers(1, &it.second);
    for (auto it : m_Inss)
        glDeleteBuffers(1, &it.second);
    for (auto it : m_UBOs)
        glDeleteBuffers(1, &it.second);

    for (auto it : m_textures)
        glDeleteTextures(1, &it.second);

    for (auto it : m_frames)
    {
        glDeleteFramebuffers(1, &it.second.frameID);
        glDeleteRenderbuffers(1, &it.second.RBO);
        glDeleteTextures(1, &it.second.textureID);
    }

    if (g_aisContext)
    {
        delete g_aisContext;
        g_aisContext = nullptr;
    }

    // 销毁资源（不能使用 delete 删除 glfw 窗口）
    glfwDestroyWindow(m_window);
    glfwTerminate();
}

void GL_Context::gen_shader(std::string i, bool geom)
{
    // 检查是否已经创建
    if (m_shaders.find(i) != m_shaders.end())
        return;

    std::string vertexPath = "shader/" + i + ".vert";
    std::string fragmentPath = "shader/" + i + ".frag";
    std::string geometryPath = "shader/" + i + ".geom";

    if (geom)
        m_shaders[i].reset(new GL_Shader(vertexPath.c_str(), fragmentPath.c_str(), geometryPath.c_str()));
    else
        m_shaders[i].reset(new GL_Shader(vertexPath.c_str(), fragmentPath.c_str(), nullptr));
}

bool GL_Context::bind_shader(std::string i)
{
    auto it = m_shaders.find(i);
    if (it == m_shaders.end())
    {
        std::cerr << "Program don't exist." << std::endl;
        return false;
    }

    m_shader = it->second;
    m_shader->enable();
    return true;
}

bool GL_Context::remove_shader(std::string i)
{
    auto it = m_shaders.find(i);
    if (it == m_shaders.end())
    {
        std::cerr << "Program don't exist." << std::endl;
        return false;
    }

    m_shaders.erase(it);
    return true;
}

void GL_Context::gen_array(int i)
{
    unsigned int VAO, VBO, EBO, Ins;

    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    glGenBuffers(1, &EBO);
    glGenBuffers(1, &Ins);

    m_VAOs[i] = VAO;
    m_VBOs[i] = VBO;
    m_EBOs[i] = EBO;
    m_Inss[i] = Ins;
}

bool GL_Context::bind_array(int i)
{
    auto it = m_VAOs.find(i);
    if (it == m_VAOs.end())
    {
        std::cerr << "Array don't exist." << std::endl;
        return false;
    }

    glBindVertexArray(m_VAOs[i]);
    glBindBuffer(GL_ARRAY_BUFFER, m_VBOs[i]);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_EBOs[i]);
    return true;
}

bool GL_Context::bind_ins_array(int i)
{
    auto it = m_Inss.find(i);
    if (it == m_Inss.end())
    {
        std::cerr << "Instance don't exist." << std::endl;
        return false;
    }

    glBindBuffer(GL_ARRAY_BUFFER, it->second);
    return true;
}

bool GL_Context::remove_array(int i)
{
    auto it = m_VAOs.find(i);
    if (it == m_VAOs.end())
    {
        std::cerr << "Array don't exist." << std::endl;
        return false;
    }

    glDeleteVertexArrays(1, &m_VAOs[i]);
    glDeleteBuffers(1, &m_VBOs[i]);
    glDeleteBuffers(1, &m_EBOs[i]);
    glDeleteBuffers(1, &m_Inss[i]);

    m_VAOs.erase(i);
    m_VBOs.erase(i);
    m_EBOs.erase(i);
    m_Inss.erase(i);
    return true;
}

void GL_Context::unbind_array()
{
    glBindVertexArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}

void GL_Context::gen_UBO(int i)
{
    unsigned int UBO;

    // 生成后直接绑定到 i 号位
    glGenBuffers(1, &UBO);
    glBindBufferBase(GL_UNIFORM_BUFFER, i, UBO);

    m_UBOs[i] = UBO;
}

bool GL_Context::bind_UBO(int i)
{
    auto it = m_UBOs.find(i);
    if (it == m_UBOs.end())
    {
        std::cerr << "UBO don't exist." << std::endl;
        return false;
    }

    glBindBuffer(GL_UNIFORM_BUFFER, it->second);
    return true;
}

bool GL_Context::remove_UBO(int i)
{
    auto it = m_UBOs.find(i);
    if (it == m_UBOs.end())
    {
        std::cerr << "UBO don't exist." << std::endl;
        return false;
    }

    glDeleteBuffers(1, &it->second);
    m_UBOs.erase(it);
    return true;
}

void GL_Context::unbind_UBO()
{
    glBindBuffer(GL_UNIFORM_BUFFER, 0);
}

void GL_Context::gen_tex(int i)
{
    unsigned int texture;
    glGenTextures(1, &texture);
    m_textures[i] = texture;
}

void GL_Context::load_tex1d(Texture texture, float a)
{
    float rgba[] = {1, 1, 1, a};

    // 生成纹理
    float texArray[16][4];
    for (int i = 0; i < 16; i++)
    {
        if (i % 2 == 0)
        {
            if (texture == Texture::Dash)
            {
                texArray[i][0] = 0;
                texArray[i][1] = 0;
                texArray[i][2] = 0;
                texArray[i][3] = 1;
            }
            if (texture == Texture::Snipple)
            {
                texArray[i][0] = 0;
                texArray[i][1] = 0;
                texArray[i][2] = 0;
                texArray[i][3] = 0;
            }
        }
        else
        {
            texArray[i][0] = rgba[0];
            texArray[i][1] = rgba[1];
            texArray[i][2] = rgba[2];
            texArray[i][3] = rgba[3];
        }
    }

    glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 16, 0, GL_RGBA, GL_FLOAT, texArray);
    glGenerateMipmap(GL_TEXTURE_1D);

    glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}

void GL_Context::load_tex2d(const char *file)
{
    // 加载图片前翻转图片
    stbi_set_flip_vertically_on_load(true);

    // glTexImage2D 生成纹理，然后 glGenerateMipmap 产生多级渐远纹理
    int width, height, nrComponents;
    unsigned char *data = stbi_load(file, &width, &height, &nrComponents, 0);
    if (data)
    {
        // 判定图像数据类型
        GLenum format = GL_RGB;
        if (nrComponents == 1)
            format = GL_RED;
        else if (nrComponents == 3)
            format = GL_RGB;
        else if (nrComponents == 4)
            format = GL_RGBA;

        glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
        glGenerateMipmap(GL_TEXTURE_2D);

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

        stbi_image_free(data);
    }
    else
    {
        std::cerr << "Texture failed to load at path: " << file;
        stbi_image_free(data);
    }
}

void GL_Context::load_tex2d(Texture texture)
{
    float rgba[] = {0, 0, 0, 1};

    switch (texture)
    {
    case Texture::Yellow2d:
        rgba[0] = rgba[1] = 1;
        break;
    case Texture::Red2d:
        rgba[0] = 1;
        break;
    case Texture::Green2d:
        rgba[1] = 1;
        break;
    case Texture::Blue2d:
        rgba[2] = 1;
        break;
    case Texture::White2d:
        rgba[0] = rgba[1] = rgba[2] = 1;
        break;
    case Texture::Black2d:
    case Texture::Chess:
        break;
    default:
        std::cerr << "Texture don't exists." << std::endl;
        break;
    }

    // 生成纹理
    float texArray[16][16][4];
    for (int i = 0; i < 16; i++)
    {
        for (int j = 0; j < 16; j++)
        {
            if (texture == Texture::Chess && (i + j) % 2 == 0)
            {
                texArray[i][j][0] = 1;
                texArray[i][j][1] = 1;
                texArray[i][j][2] = 1;
                texArray[i][j][3] = 1;
            }
            else
            {
                texArray[i][j][0] = rgba[0];
                texArray[i][j][1] = rgba[1];
                texArray[i][j][2] = rgba[2];
                texArray[i][j][3] = rgba[3];
            }
        }
    }

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_FLOAT, texArray);
    glGenerateMipmap(GL_TEXTURE_2D);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}

void GL_Context::load_tex3d(std::vector<const char *> files)
{
    // 取消纹理翻转
    stbi_set_flip_vertically_on_load(false);

    // glTexImage2D 生成纹理，然后 glGenerateMipmap 产生多级渐远纹理
    int width, height, nrComponents;
    for (unsigned int i = 0; i < files.size(); i++)
    {
        unsigned char *data = stbi_load(files[i], &width, &height, &nrComponents, 0);

        if (data)
        {
            // 判定图像数据类型
            GLenum format = GL_RGB;
            if (nrComponents == 1)
                format = GL_RED;
            else if (nrComponents == 3)
                format = GL_RGB;
            else if (nrComponents == 4)
                format = GL_RGBA;

            glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE,
                         data);
            glGenerateMipmap(GL_TEXTURE_CUBE_MAP);

            stbi_image_free(data);
        }
        else
        {
            std::cerr << "Texture failed to load at path: " << files[i];
            stbi_image_free(data);
        }
    }

    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}

void GL_Context::activate_tex(int i)
{
    glActiveTexture(GL_TEXTURE0 + i);
}

bool GL_Context::bind_tex1d(int i)
{
    auto it = m_textures.find(i);
    if (it == m_textures.end())
    {
        std::cerr << "Texture don't exist." << std::endl;
        return false;
    }

    glBindTexture(GL_TEXTURE_1D, it->second);
    return true;
}

bool GL_Context::bind_tex2d(int i)
{
    auto it = m_textures.find(i);
    if (it == m_textures.end())
    {
        std::cerr << "Texture don't exist." << std::endl;
        return false;
    }

    glBindTexture(GL_TEXTURE_2D, it->second);
    return true;
}

bool GL_Context::bind_tex3d(int i)
{
    auto it = m_textures.find(i);
    if (it == m_textures.end())
    {
        std::cerr << "Texture don't exist." << std::endl;
        return false;
    }

    glBindTexture(GL_TEXTURE_CUBE_MAP, it->second);
    return true;
}

bool GL_Context::remove_tex(int i)
{
    auto it = m_textures.find(i);
    if (it == m_textures.end())
    {
        std::cerr << "Texture don't exist." << std::endl;
        return false;
    }

    glDeleteTextures(1, &it->second);
    m_textures.erase(it);
    return true;
}

void GL_Context::unbind_tex()
{
    glBindTexture(GL_TEXTURE_1D, 0);
    glBindTexture(GL_TEXTURE_2D, 0);
    glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
}

void GL_Context::gen_frame(int i, int w, int h)
{
    FrameData frameID;
    glGenFramebuffers(1, &frameID.frameID);
    glGenTextures(1, &frameID.textureID);
    glGenRenderbuffers(1, &frameID.RBO);
    m_frames[i] = frameID;

    glBindFramebuffer(GL_FRAMEBUFFER, frameID.frameID);

    // 创建纹理附件
    glBindTexture(GL_TEXTURE_2D, frameID.textureID);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glBindTexture(GL_TEXTURE_2D, 0);

    // 创建渲染缓冲对象
    glBindRenderbuffer(GL_RENDERBUFFER, frameID.RBO);
    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, w, h);
    glBindRenderbuffer(GL_RENDERBUFFER, 0);

    // 绑定到当前帧缓冲
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, frameID.textureID, 0);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, frameID.RBO);

    // 检查帧缓冲是否完整
    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
        std::cerr << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl;

    // 注意 Qt 中解绑需要将其绑定到默认的帧缓冲中，而不是 0
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

bool GL_Context::bind_frame(int i)
{
    auto it = m_frames.find(i);
    if (it == m_frames.end())
    {
        std::cerr << "Frame don't exist." << std::endl;
        return false;
    }

    glBindFramebuffer(GL_FRAMEBUFFER, it->second.frameID);
    return true;
}

unsigned int GL_Context::frame_tex(int i)
{
    auto it = m_frames.find(i);
    if (it == m_frames.end())
    {
        std::cerr << "Frame don't exist." << std::endl;
        return 0;
    }

    return it->second.textureID;
}

bool GL_Context::remove_frame(int i)
{
    auto it = m_frames.find(i);
    if (it == m_frames.end())
    {
        std::cerr << "Frame don't exist." << std::endl;
        return false;
    }

    glDeleteFramebuffers(1, &it->second.frameID);
    glDeleteRenderbuffers(1, &it->second.RBO);
    glDeleteTextures(1, &it->second.textureID);

    m_frames.erase(it);
    return true;
}

void GL_Context::unbind_frame()
{
    // 注意 Qt 中解绑需要将其绑定到默认的帧缓冲中，而不是 0
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

void GL_Context::draw(int width, int height) const
{
    // 将会调用回调函数
    glfwSetWindowSize(m_window, width, height);

    // 启用混合、深度测试、模板测试、多重采样
    glEnable(GL_BLEND);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_STENCIL_TEST);
    glEnable(GL_MULTISAMPLE);

    glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    // 背景色
    glClearColor(0.2f, 0.2f, 0.2f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // 交互绘制
    g_aisContext->paint_event();

    glfwSwapBuffers(m_window);
    glfwPollEvents();
}

std::shared_ptr<GL_Shader> GL_Context::shader() const
{
    return m_shader;
}

GLFWwindow *GL_Context::window() const
{
    return m_window;
}

AIS_Context *GL_Context::context() const
{
    return g_aisContext;
}

int GL_Context::message_loop()
{
    // 开启消息循环
    while (!glfwWindowShouldClose(m_window))
    {
        // 检查键盘输入，如果为 ESC 就退出
        if (glfwGetKey(m_window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
            glfwSetWindowShouldClose(m_window, true);

        int width, height;
        glfwGetFramebufferSize(m_window, &width, &height);

        draw(width, height);
    }

    return 0;
}